/*
向内核动态申请主设备号
实现文件操作接口
动态创建设备文件
	创建设备类
	创建设备文件

容错处理
面向对象的封装

硬件初始化

*/

#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/gpio.h>
#include <asm/uaccess.h>



//采用面向对象方式来封装这些全局变量

struct rk3399_led {
	int major ;
	struct class *cls;
	struct device *dev;
	unsigned int gpio_led2_green; //GPIO1_C7 55
	int value; //保存用户空间传来的数据
}; 

static struct rk3399_led *led_device; //声明一个对象指针，没有创建对象

static int led_open (struct inode *inode, struct file *filp)
{
	
	//printk("<kernel> call %s() @ %d\n ", __func__, __LINE__);
	return 0;

}
static int led_close(struct inode *inode, struct file *filp)
{
	//设置引脚为输出功能低电平
	//gpio_direction_output(led_device->gpio_led2_green, 0);
	//printk("<kernel> call %s() @ %d\n ", __func__, __LINE__);
	return 0;
}


ssize_t led_write (struct file *filp, const char __user *buf, size_t size, loff_t *flags)
{
	int ret;
	ret = copy_from_user(&led_device->value, buf, size);
	if(ret) {
		printk("<kernel> copy_from_user fail\n");
		return -EFAULT;
	}
	printk("<kernel> value=%d\n", led_device->value);
	if (led_device->value) {
		//设置引脚为输出功能高电平
		gpio_direction_output(led_device->gpio_led2_green, 1);
	} else {
		//设置引脚为输出功能高电平
		gpio_direction_output(led_device->gpio_led2_green, 0);
	}
	//printk("<kernel> call %s() @ %d\n ", __func__, __LINE__);
	//printk("<kernel> size=%ld\n", size);
	return size;
}


static ssize_t led_read (struct file *filp, char __user *buf, size_t size, loff_t *flags)
{
	int ret;
	int read_value;
	read_value = gpio_get_value(led_device->gpio_led2_green);
	
	ret = copy_to_user(buf, &read_value, size);
	if (ret>0) {
		printk("<kernel> copy_to_user fail\n");

	
		return -EFAULT;
	}
	memset(&read_value, 0, sizeof(read_value)); //清0
	//printk("<kernel> call %s() @ %d\n ", __func__, __LINE__);
	return size;
}



static struct file_operations fops = {
	.owner = THIS_MODULE, //避免卸载模块
	.open = led_open,
	.release = led_close,	
	.write = led_write,
	.read = led_read,
};


//定义入口函数 添加模块时内核会调用
static int __init led_init(void)  //__init 优化  一次性
{
	int ret;
	
	led_device = kmalloc(sizeof(struct rk3399_led), GFP_KERNEL);
	if(led_device == NULL) {
		ret = -ENOMEM;
		return ret;
	}
	
	led_device->major = register_chrdev(0, "led_drv", &fops);
	if(led_device->major<0) {
		printk("<kernel> register_chrdev fail\n");
		ret= -EBUSY;
		goto err_register_chrdev;
	}

	printk("<kernel> major=%d\n", led_device->major);

	printk("<kernel> call %s() @ %d\n ", __func__, __LINE__);
	

	//动态创建设备文件
	//创建设备类
	led_device->cls = class_create(THIS_MODULE, "led_cls");
	if (IS_ERR(led_device->cls)) {
		printk("<kernel> class_create fail\n");
		ret = PTR_ERR(led_device->cls);
		goto err_class_create;
	}
	//创建设备文件
	led_device->dev = device_create(led_device->cls, NULL, MKDEV(led_device->major, 0), NULL, "led"); // /dev/led
	if (IS_ERR(led_device->dev)) {
		printk("<kernel> device_create fail\n");
		ret = PTR_ERR(led_device->dev);
		goto err_device_create;
	}

	//硬件初始化
	led_device->gpio_led2_green = 55;
	//申请引脚
	ret = gpio_request(led_device->gpio_led2_green, "led2_green");
	if(ret) {
		printk("<kernel> gpio_request fail\n");
		goto err_gpio_request;
	}
	//设置引脚为输出功能默认低电平
	gpio_direction_output(led_device->gpio_led2_green, 0);
	//释放引脚
	gpio_free(led_device->gpio_led2_green);

	return 0;
err_gpio_request:
	device_destroy(led_device->cls, MKDEV(led_device->major, 0));
err_device_create:
	class_destroy(led_device->cls);
err_class_create:
	unregister_chrdev(led_device->major, "led_drv");
err_register_chrdev:
	kfree(led_device);

	return ret;

}


//定义出口函数 卸载模块时内核会调用
static void __exit led_exit(void) //_exit 优化 一次性
{
	device_destroy(led_device->cls, MKDEV(led_device->major, 0));
	class_destroy(led_device->cls);
	unregister_chrdev(led_device->major, "led_drv");
	kfree(led_device);
	printk("<kernel> call %s() @ %d\n ", __FUNCTION__, __LINE__);
}


module_init(led_init); //告诉内核我这个模块程序 入口（初始化）函数是led_init
module_exit(led_exit); //告诉内核我这个模块程序 入口（初始化）函数是led_exit
MODULE_AUTHOR("robin");
MODULE_DESCRIPTION("my first led world driver");
MODULE_LICENSE("GPL"); //开源许可协议


